/*
Rami El Mawas
rami.contact@gmail.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

/* RPN Calculator */
enum NodeTypes { NOTYPE, NUM, CONST, ARTH, FUNC, ATTR };
enum NodeOp {
  NOOP,
  // ARTH
  PLUS, MINUS, MULTIPLY, DIVIDE,
  // CONST
  PI, E,
  // FUNC
  COS, SIN, TAN, ASIN, ACOS, ATAN, COSH, SINH, TANH, EXP, LOG, LOG10, CEIL, FABS, FLOOR, SQRT
};

/* RPN Calculator */
extern void vStrcpy( char ** cppDst, const char * cpSrc );


class Node {
  public:
    Node( char *cpString );
    ~Node();

    Node *nodepNext;  // pointer to the next node in the stack
    char *cpValue;    // pointer to the value of the token striped from helper modifiers (#, $, @)
    double dValue;    // value of the node. either a Numeric type or the value after evaluate was run
    NodeTypes iType;  // type of the node 
    NodeOp iOp;       // type of operator
    int iAttrIndex;   // index of sphinx attribute if it is an ATTR node
    int iFootPrint;   // footprint of evaluating the node in a stack
    int iError;       // type of error

    void vPrint();
    double dConstCalc();
    double dArthCalc( double val1, double val2 );
    double dFuncCalc( double val );
    void vGetOp( char *op );

  private:
    static const double dpConstants[];
    void vSetType( char *cpString );
    int iValidValue( double total );  // return 0 if isnan or isinf evaulate to true. used in /, tan, atan, log, log10, sqrt
};

template <class T>
class Stack {
  public:
    Stack( const char *cpEquationNew, T* tpAttributes );  // Stack Constructor
    ~Stack(); // Stack destructor
    void vPrint();  // prints the stack. Loop over all the nodes and prints each
    double dEvaluate(); // evaluate the stack by evaluating the first node
    void vDispatchError( char ** ); // dispatch stack construction error number to the corresponding error string
    int iError; // type of error

  private:
    char * cpEquation;      // string that contains the equation
    Node * nodepStack;      // pointer to the top of the stack
    Node * temp;            // temporary pointer to the node in evaluation
    T * tpAttributes;
    int iSize;              // number of nodes in the stack
    int iAttrIndex;         // index of sphinx attribute if it is an ATTR node
    int iSyntaxCheck;       // counter to check the syntax of the equation
    static const char *cppErrors[]; // errors that stack construction can result in

    void vAdd( char *string );
    double eval();
};

template <class T>
Stack<T>::Stack( const char * cpEquationNew, T* tpAttributesInput ): iError( 0 ), nodepStack( NULL ), iSize( 0), iSyntaxCheck( 0 ) { 
  char * cpEquationTemp = NULL, * cpEq;
  const char * cpTok = " ";
  tpAttributes = tpAttributesInput;
  vStrcpy( &cpEquation, cpEquationNew );
  vStrcpy( &cpEquationTemp, cpEquationNew );
  cpEq = strtok( cpEquationTemp, cpTok );
  while ( cpEq != NULL ) { 
    vAdd( cpEq );
    cpEq = strtok( NULL, cpTok );
  }
  delete [] cpEquationTemp;
  if( !iError && iSyntaxCheck != 1 ) 
    iError = 6;
}

template <class T>
Stack<T>::~Stack() {
   printf( "~Stack: <%s>\n", cpEquation );
   delete [] cpEquation;
   Node * t1 = nodepStack, * t2;
   while( t1 != NULL ) {
     t2 = t1->nodepNext;
     delete t1;
     t1 = t2;
   }
   fflush( stdout );
}

template <class T>
double Stack<T>::dEvaluate() {
  double total = 0.0;
  if( nodepStack != NULL ) {
    temp = nodepStack;
    total = eval();
  }
  return total;
}

template <class T>
void Stack<T>::vAdd( char * string ) {
  Node *nodeNew = new Node( string );
  if( nodeNew->iError ) {
    iError = nodeNew->iError;
  }
  iSyntaxCheck += nodeNew->iFootPrint;
  if( iSize == 0 )
    nodepStack = nodeNew;
  else {
    nodeNew->nodepNext = nodepStack;
    nodepStack = nodeNew;
  }
  iSize++;
}

template <class T>
double Stack<T>::eval() {
  Node * current = temp;
  double total = 0.0;
  int iType = current->iType;
  switch( iType ) {
    case NUM:
    case CONST:
      total = current->dValue;
      break;
    case ARTH:
      {
      temp = temp->nodepNext;
      double val1 = eval();
      temp = temp->nodepNext;
      double val2 = eval();
      total = current->dArthCalc( val1, val2 );
      }
      break;
    case FUNC:
      {
        temp = temp->nodepNext;
        double val = eval();
        total = current->dFuncCalc( val );
      }
      break;
    case ATTR:
      total = tpAttributes[current->iAttrIndex];
      break;
  }
  if( current->iError )
    iError = current->iError;
  return current->dValue = total;
}

/* end of RPN */
